package net.towee.client.managers.impl;

import java.util.List;

import net.towee.client.event.core.ContactAddedEvent;
import net.towee.client.event.core.ContactRemovedEvent;
import net.towee.client.event.core.ContactsGroupChangedEvent;
import net.towee.client.event.core.ContactsGroupDeletedEvent;
import net.towee.client.event.core.UserAddedToContactsEvent;
import net.towee.client.event.core.UserRemovedFromContactsEvent;
import net.towee.client.managers.AccountHelper;
import net.towee.client.managers.ContactsManager;
import net.towee.model.account.AccountIdentity;
import net.towee.model.account.ContactsGroup;
import net.towee.model.account.impl.AccountIdentityImpl;
import net.towee.model.utils.Pair;
import net.towee.server.rpc.services.ToweeServiceAsync;
import net.towee.shared.exceptions.NameExistsException;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.inject.Inject;

public class ContactsManagerImpl implements ContactsManager {
	private final ToweeServiceAsync service;
	private final HandlerManager eventBus;
	private final AccountHelper accountHelper;

	@Inject
	public ContactsManagerImpl(ToweeServiceAsync service, HandlerManager eventBus, AccountHelper helper) {
		this.eventBus = eventBus;
		this.service = service;
		this.accountHelper = helper;
		
		createPushHandlers();
	}
	
	private void createPushHandlers() {
		service.onUserAddedToContacts(null, null, new AsyncCallback< Pair<AccountIdentity, String> >(){
			@Override
			public void onFailure(Throwable caught) {}

			@Override
			public void onSuccess(Pair<AccountIdentity, String> result) {
				eventBus.fireEvent( new UserAddedToContactsEvent(result.getFirst(), result.getSecond()) );
			}
			
		});
		
		service.onUserRemovedFromContacts(null, null, new AsyncCallback< Pair<AccountIdentity, String> >() {
			@Override
			public void onFailure(Throwable caught) {}

			@Override
			public void onSuccess(Pair<AccountIdentity, String> result) {
				eventBus.fireEvent( new UserRemovedFromContactsEvent(result.getFirst(), result.getSecond()));	
			}
		});
	}

	@Override
	public void createContactsGroup(String name, final AsyncCallback<ContactsGroup> handler) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(name), "null name" );
		
		service.createContactsGroup(name, new AsyncCallback<ContactsGroup>(){
			@Override
			public void onFailure(Throwable caught) {
				if(caught instanceof NameExistsException)
					handler.onFailure(caught);
			}

			@Override
			public void onSuccess(ContactsGroup result) {
				// Add to local stored account
				accountHelper.getAccount().getContacts().addContactsGroup( result );
				
				// Fire an event
				eventBus.fireEvent(new ContactsGroupChangedEvent( result ));

				// Invoke success method
				handler.onSuccess(result);
			}
		});
	}

	@Override
	public void deleteContactsGroup(final String id, final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(id), "null id" );
		
		service.removeContactsGroup(id, new AsyncCallback<Boolean>(){

			@Override
			public void onFailure(Throwable caught) {
				handler.onFailure(caught);
			}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "delete contact group fail");
				
				// Get deleted contacts group
				ContactsGroup g = accountHelper.getAccount().getContacts().getContactsGroup(id);
				
				// Remove from local stored account
				accountHelper.getAccount().getContacts().removeContactsGroup(id);
				
				// Fire an event
				eventBus.fireEvent(new ContactsGroupDeletedEvent( g ));
				handler.onSuccess(true);
			}
		});
	}

	@Override
	public void renameContactsGroup(final String id, final String newName, final AsyncCallback<ContactsGroup> handler) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(id), "null contacts group id" );
		Preconditions.checkArgument( !Strings.isNullOrEmpty(newName), "null contacts group name" );
		
		service.renameContactsGroup(id, newName, new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {}
			
			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "rename contact group fail");
				
				// Get renamed group
				ContactsGroup g = accountHelper.getAccount().getContacts().getContactsGroup(id);
				g.setName(newName);
				
				// Fire an event
				eventBus.fireEvent(new ContactsGroupChangedEvent( g ));
				handler.onSuccess(g);
			}
		});
	}

	@Override
	public void addContactsToGroup(final String id, final List<AccountIdentity> accounts,
			final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(id), "null contacts group id" );
		Preconditions.checkNotNull(accounts, "null accounts list");
		
		service.addContactsToGroup(id, accounts, new AsyncCallback<Boolean>(){
			@Override
			public void onFailure(Throwable caught) {}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "add contact fail");
				
				// Get group of contacts
				ContactsGroup g = accountHelper.getAccount().getContacts().getContactsGroup(id);
				
				// Add contacts to local stored account
				for(AccountIdentity idnt : accounts) {
					g.addContact(idnt);
					eventBus.fireEvent(new ContactAddedEvent(idnt, id));
				}
				
				handler.onSuccess(true);
			}
		});
	}

	@Override
	public void removeContactFromGroup(final String id, final List<String> accountIds, final AsyncCallback<Boolean> handler) {
		Preconditions.checkArgument( !Strings.isNullOrEmpty(id), "null contacts group id" );
		Preconditions.checkNotNull(accountIds, "null accounts list");
		
		service.removeContactsForomGroup(id, accountIds, new AsyncCallback<Boolean>() {
			@Override
			public void onFailure(Throwable caught) {}

			@Override
			public void onSuccess(Boolean result) {
				Preconditions.checkArgument(result, "remove contact fail");
				
				// Get group of contacts
				ContactsGroup g = accountHelper.getAccount().getContacts().getContactsGroup(id);
				
				// Add contacts to local stored account
				for(String idnt : accountIds) {
					g.deleteContact(new AccountIdentityImpl(idnt));
					eventBus.fireEvent(new ContactRemovedEvent(idnt, id));
				}
				
				handler.onSuccess(true);
			}
		});
	}
	
}